﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Diagnostics;
using WPFExtensions.Helpers;

namespace WPFExtensions.AttachedBehaviours
{
	[DebuggerNonUserCode]
	public class TreeViewSelectionBehaviour : FrameworkElementAttachedBehaviourBase<TreeView>
	{
		#region Attached Dependency Properties
		private static void ManageAttach( DependencyObject d )
		{
			var tv = d as TreeView;
			if ( tv == null )
				return;
			var shouldAttach = GetAutomaticBringIntoViewSelectedItem( tv ) || GetManageSelectedItemRoute( tv );

			if ( shouldAttach )
				Attach<TreeViewSelectionBehaviour>( tv );
			else
				Detach<TreeViewSelectionBehaviour>( tv );
		}


		public static bool GetAutomaticBringIntoViewSelectedItem( DependencyObject obj )
		{
			return (bool)obj.GetValue( AutomaticBringIntoViewSelectedItemProperty );
		}

		public static void SetAutomaticBringIntoViewSelectedItem( DependencyObject obj, bool value )
		{
			obj.SetValue( AutomaticBringIntoViewSelectedItemProperty, value );
		}

		// Using a DependencyProperty as the backing store for AutomaticBringIntoViewSelectedItem.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty AutomaticBringIntoViewSelectedItemProperty =
			DependencyProperty.RegisterAttached( "AutomaticBringIntoViewSelectedItem", typeof( bool ), typeof( TreeViewSelectionBehaviour ), new UIPropertyMetadata( false, AutomaticBringIntoViewSelectedItem_PropertyChanged ) );

		private static void AutomaticBringIntoViewSelectedItem_PropertyChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e )
		{
			ManageAttach( obj );
		}


		[AttachedPropertyBrowsableForType( typeof( TreeView ) )]
		public static bool GetManageSelectedItemRoute( DependencyObject obj )
		{
			return (bool)obj.GetValue( ManageSelectedItemRouteProperty );
		}

		public static void SetManageSelectedItemRoute( DependencyObject obj, bool value )
		{
			obj.SetValue( ManageSelectedItemRouteProperty, value );
		}

		// Using a DependencyProperty as the backing store for ManagedSelection.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty ManageSelectedItemRouteProperty =
			DependencyProperty.RegisterAttached( "ManageSelectedItemRoute", typeof( bool ), typeof( TreeViewSelectionBehaviour ), new UIPropertyMetadata( false, ManageSelectedItemRoute_PropertyChanged ) );

		private static void ManageSelectedItemRoute_PropertyChanged( DependencyObject obj, DependencyPropertyChangedEventArgs e )
		{
			ManageAttach( obj );
		}

		public static IEnumerable<object> GetSelectedItemRoute( DependencyObject obj )
		{
			return (IEnumerable<object>)obj.GetValue( SelectedItemRouteProperty );
		}

		public static void SetSelectedItemRoute( DependencyObject obj, IEnumerable<object> value )
		{
			obj.SetValue( SelectedItemRouteProperty, value );
		}

		// Using a DependencyProperty as the backing store for SelectedItemRoute.  This enables animation, styling, binding, etc...
		public static readonly DependencyProperty SelectedItemRouteProperty =
			DependencyProperty.RegisterAttached( "SelectedItemRoute", typeof( IEnumerable<object> ), typeof( TreeViewSelectionBehaviour ), new UIPropertyMetadata( null, null, SelectedItemRoute_Coerce ) );

		private static object SelectedItemRoute_Coerce( DependencyObject d, object baseValue )
		{
			var treeView = (TreeView)d;
			var helper = Get<TreeViewSelectionBehaviour>( treeView );
			if ( helper == null )
				return SelectedItemRouteProperty.DefaultMetadata.DefaultValue;

			if ( helper.allowSelectedItemRouteChange || SelectedItemRouteProperty.DefaultMetadata.DefaultValue == baseValue )
				return baseValue;

			throw new NotSupportedException( "This is a one-way dependency property (it behaves like a read-only dependency property, but it can be bound with [Mode=OneWayToSource])." );
		}
		#endregion

		private bool allowSelectedItemRouteChange = false;

		public TreeViewSelectionBehaviour( TreeView treeView )
			: base( treeView )
		{
		}

		protected override void OnAttach()
		{
			Element.SelectedItemChanged += SelectedItemChanged;
		}

		protected override void OnDetach()
		{
			Element.SelectedItemChanged -= SelectedItemChanged;
		}

		private void SelectedItemChanged( object sender, RoutedPropertyChangedEventArgs<object> e )
		{
			Debug.Assert( Element == sender, "The SelectedItemChanged event wasn't generated by the attached TreeView!" );

			var selectedTreeViewItem = Element.FindContainerHierarchically<TreeViewItem>( Element.SelectedItem );
			if ( selectedTreeViewItem != null )
			{
				if ( GetAutomaticBringIntoViewSelectedItem( Element ) )
					selectedTreeViewItem.BringIntoView();

				if ( GetAutomaticBringIntoViewSelectedItem( Element ) ||
					GetManageSelectedItemRoute( Element ) )
				{
					allowSelectedItemRouteChange = true;
					SetSelectedItemRoute( Element, selectedTreeViewItem.GetItemRouteFromTreeViewItem() );
					allowSelectedItemRouteChange = false;
				}
			}
		}
	}
}
